﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.ComponentModel;
using System.Windows.Media;
using System.Windows.Input;
using System.Diagnostics;

namespace Wilke.Interactive.Drone.PlayGround.Controls
{
    public class PieMenu : Panel
    {
        public event EventHandler<SegmentClickedEventArgs> SegmentClicked;

        public static readonly DependencyProperty ClippingRadiusProperty;

        [Bindable(true)]
        public double ClippingRadius
        {
            get
            {
                return (double)base.GetValue(PieMenu.ClippingRadiusProperty);
            }
            set
            {
                base.SetValue(PieMenu.ClippingRadiusProperty, value);
            }
        }

        private bool ismouseleftdown = false;

        static PieMenu()
        {
            PieMenu.ClippingRadiusProperty = DependencyProperty.Register("ClippingRadius", typeof(double), typeof(PieMenu), new FrameworkPropertyMetadata(20.0));
        }

        protected override Size MeasureOverride(Size availablesize)
        {
            foreach (UIElement uie in Children)
            {
                uie.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
            }

            return availablesize;
        }

        protected override Size ArrangeOverride(Size finalsize)
        {
            double radiusX = this.DesiredSize.Width / 2.0;
            double radiusY = this.DesiredSize.Height;
            Point center = new Point(radiusX, 0);

            double angle = 0;
            double anglestep = 0;

            if (this.Children.Count != 0)
            {
                anglestep = 180 / (double)this.Children.Count;
            }

            double deg2rad = Math.PI / 180.0;

            foreach (UIElement uie in Children)
            {
                double a = (angle + anglestep / 2) * deg2rad;

                uie.Arrange(new Rect(Point.Add(center, new Vector((radiusX + ClippingRadius) * Math.Cos(a) / 2.0 - uie.DesiredSize.Width / 2.0, (radiusY + ClippingRadius) * Math.Sin(a) / 2.0 - uie.DesiredSize.Height / 2.0)), uie.DesiredSize));

                angle += anglestep;
            }

            return finalsize;
        }

        protected override void OnMouseEnter(System.Windows.Input.MouseEventArgs e)
        {
            base.OnMouseEnter(e);

            this.InvalidateVisual();
        }

        protected override void OnMouseLeave(System.Windows.Input.MouseEventArgs e)
        {
            base.OnMouseLeave(e);

            this.InvalidateVisual();
        }

        protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);

            this.ismouseleftdown = true;

            this.InvalidateVisual();
        }

        protected override void OnMouseLeftButtonUp(System.Windows.Input.MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonUp(e);

            this.ismouseleftdown = false;

            this.InvalidateVisual();
        }

        protected override void OnMouseMove(System.Windows.Input.MouseEventArgs e)
        {
            base.OnMouseMove(e);

            this.InvalidateVisual();
        }

        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
            base.OnMouseUp(e);

            if (SegmentClicked != null)
            {
                SegmentClicked(this, new SegmentClickedEventArgs() { SegmentIndex = GetCurrentSegmentIndex() });
            }
        }

        protected override void OnRender(DrawingContext dc)
        {
            double radiusX = this.DesiredSize.Width / 2.0;
            double radiusY = this.DesiredSize.Height;
            Point center = new Point(radiusX, 0);
            double angle = 0;
            double anglestep = 0;

            dc.PushClip(new CombinedGeometry(GeometryCombineMode.Exclude, DrawingExtensions.GetArcGeometry(center, radiusX, radiusY), DrawingExtensions.GetArcGeometry(center, ClippingRadius, ClippingRadius)));
            dc.DrawArc(new SolidColorBrush(Colors.Silver), new Pen(new SolidColorBrush(Colors.DarkSlateBlue), 1), center, radiusX, radiusY);

            #region Calculate selected segment

            if (this.Children.Count != 0)
            {
                anglestep = 180 / (double)this.Children.Count;
            }

            double deg2rad = Math.PI / 180.0;

            double startAngle = 0;
            double endAngle = 0;

            startAngle = (this.Children.Count - GetCurrentSegmentIndex() - 1) * anglestep * deg2rad;
            endAngle = startAngle + (anglestep * deg2rad);

            #endregion

            #region Draw segment dividers

            //foreach (UIElement uie in this.Children)
            for (int index = 1; index < this.Children.Count; index++)
            {
                double a = (angle + anglestep) * deg2rad;
                dc.DrawLine(new Pen(new SolidColorBrush(Colors.DimGray), 1.0), center, Point.Add(center, new Vector(radiusX * Math.Cos(a), radiusY * Math.Sin(a))));
                angle += anglestep;
            }

            #endregion

            #region Highlight selected segment

            if (this.IsMouseOver)
            {
                PathGeometry path = new PathGeometry();
                PathFigure pathfig = new PathFigure();
                pathfig.StartPoint = center;
                pathfig.Segments.Add(new LineSegment(Point.Add(center, new Vector(radiusX * Math.Cos(startAngle), radiusY * Math.Sin(startAngle))), true));
                pathfig.Segments.Add(new ArcSegment(Point.Add(center, new Vector(radiusX * Math.Cos(endAngle), radiusY * Math.Sin(endAngle))), new Size(1.0, 1.0), 0.0, false, SweepDirection.Clockwise, true));
                pathfig.Segments.Add(new LineSegment(center, true));
                path.Figures.Add(pathfig);
                dc.PushClip(path);

                SolidColorBrush solidColorBrush = null;
                LinearGradientBrush linearGradientBrush = null;
                LinearGradientBrush brush = new LinearGradientBrush();
                brush.StartPoint = new Point(0, 0);
                brush.EndPoint = new Point(0, 1);

                angle = 0;

                if (this.ismouseleftdown)
                {
                    brush.GradientStops.Add(new GradientStop(Color.FromRgb(254, 216, 170), 0.0));
                    brush.GradientStops.Add(new GradientStop(Color.FromRgb(251, 181, 101), 0.4));
                    brush.GradientStops.Add(new GradientStop(Color.FromRgb(250, 157, 52), 0.4));
                    brush.GradientStops.Add(new GradientStop(Color.FromRgb(253, 238, 170), 1.0));

                    solidColorBrush = new SolidColorBrush(Color.FromRgb(171, 161, 140));
                    linearGradientBrush = new LinearGradientBrush(Color.FromRgb(223, 183, 136), Colors.Transparent, 45.0);
                }
                else
                {
                    brush.GradientStops.Add(new GradientStop(Color.FromRgb(244, 245, 255), 0.0));
                    brush.GradientStops.Add(new GradientStop(Color.FromRgb(180, 190, 248), 0.4));
                    brush.GradientStops.Add(new GradientStop(Color.FromRgb(17, 42, 175), 0.4));
                    brush.GradientStops.Add(new GradientStop(Color.FromRgb(115, 135, 242), 1.0));

                    solidColorBrush = new SolidColorBrush(Color.FromRgb(9, 23, 94));
                    linearGradientBrush = new LinearGradientBrush(Color.FromRgb(255, 255, 247), Colors.Transparent, 45.0);
                }

                dc.DrawEllipse(brush, new Pen(solidColorBrush, 1.0), center, radiusX, radiusY);
                dc.DrawEllipse(null, new Pen(linearGradientBrush, 1.0), center, radiusX - 1, radiusY - 1);

                if (ClippingRadius > 0)
                {
                    dc.DrawEllipse(null, new Pen(solidColorBrush, 1.0), center, ClippingRadius + 1, ClippingRadius + 1);
                }

                foreach (UIElement uie in this.Children)
                {
                    double a = angle * deg2rad;
                    dc.DrawLine(new Pen(solidColorBrush, 1.0), center, Point.Add(center, new Vector(radiusX * Math.Cos(a), radiusY * Math.Sin(a))));
                    angle += anglestep;
                }

                dc.Pop();
            }

            dc.Pop();

            #endregion
        }

        private int GetCurrentSegmentIndex()
        {
            double radiusX = this.DesiredSize.Width / 2.0;
            double radiusY = this.DesiredSize.Height;
            Point center = new Point(radiusX, 0);

            Point p = Mouse.GetPosition(this);
            double currentAngle = Math.Atan2(p.Y, radiusX - p.X);
            double segmentAngle = 3.14d / this.Children.Count;
            int segmentIndex = (int)(currentAngle / segmentAngle);

            return segmentIndex;
        }
    }

    public static class DrawingExtensions
    {
        public static void DrawArc(this DrawingContext drawingContext, Brush brush, Pen pen, Point center, double radiusX, double radiusY)
        {
            drawingContext.DrawGeometry(brush, pen, GetArcGeometry(center, radiusX, radiusY));
        }

        public static PathGeometry GetArcGeometry(Point center, double radiusX, double radiusY)
        {
            PathGeometry pathGeometry = new PathGeometry();
            PathFigure pathFigure = new PathFigure();
            pathGeometry.Figures.Add(pathFigure);
            pathFigure.StartPoint = new Point(center.X - radiusX, center.Y);
            ArcSegment arcSegment = new ArcSegment(new Point(center.X + radiusX, center.Y),
               new Size(radiusX, radiusY),
               0,
               false,
               SweepDirection.Counterclockwise,
               true);
            pathFigure.Segments.Add(arcSegment);

            return pathGeometry;
        }
    }
}

public class SegmentClickedEventArgs : EventArgs
{
    public int SegmentIndex { get; set; }
}
